import { getRoomDomain } from "../../../global/map";
import { jobSpread } from "../../../job/spread";
import { jobWithdraw } from "../../../job/withdraw";
import { getCreepBodyCountCoeficient, spawnCreep } from "../../../role/spawn";
import { GroupTerminal } from "../terminal_group";
import { jobRepair } from "../../../job/repair";
import { jobBuild } from "../../../job/build";
import { jobSuicide } from "../../../job/suicide";
import { getActualResourceAmount, getCapacity, getResourceAmount } from "../../../util/store";

export class GroupMaintainers extends GroupTerminal{
    // constructor(spawnRoom = undefined){
    //     super(spawnRoom);
    // }
    serialize(){
        return {
            super: super.serialize(),
        }
    }
    get targets(){
        if(this._targets !== undefined){
            return this._targets;
        }
        let structures = [];
        let domain = getRoomDomain(this.spawnRoom);
        for(let roomName of domain){
            let room = Game.rooms[roomName];
            if(!room){
                continue;
            }
            if(!this.haveTower()){
                structures.push(...room.find(FIND_STRUCTURES,{filter: jobRepair.filter}));
            }
            structures.push(...room.find(FIND_MY_CONSTRUCTION_SITES));
        }

        const sortMap = new Map([
            [STRUCTURE_ROAD,3],
            [STRUCTURE_WALL,5],
            [STRUCTURE_RAMPART,2],
        ]);
        structures.sort((a,b)=>{
            let diff = (structure)=>{
                let ans = sortMap.get(structure.structureType);
                if(ans === undefined){
                    ans = Infinity;
                }
                return ans;
            };
            return diff(a) - diff(b);
        });
        return this._targets = structures;
    }
    deserialize(obj){
        super.deserialize(obj.super);
        return this;
    }
    get maxSize(){
        // console.log(this.haveTower());
        if(this.targets.length>0 || !this.haveTower()){
            return (getCreepBodyCountCoeficient(this.spawnRoom)>=0.9 || this.spawnRoom.controller.level>4)?1:2;
        }else{
            return 0;
        }
    }
    haveTower(){
        if(this._haveTower !== undefined){
            return this._haveTower;
        }
        try{
            this.spawnRoom.find(FIND_MY_STRUCTURES,{
                filter(structure){
                    if(structure.structureType === STRUCTURE_TOWER){
                        throw new Error("not_err");
                    }
                }
            });
        }catch(e){
            if(e.message === "not_err"){
                return this._haveTower = true;
            }
            throw e;
        }
        return this._haveTower = false;
    }
    spawn(){
        let newCreep = spawnCreep(this.spawnRoom, {
            require: [MOVE, CARRY, WORK],
            move: .34,
            carry: .33,
            work: .33,
        }, {
            memory:{
                role:"maintainer"
            }
        });
        if(!(newCreep instanceof Creep)){
            return newCreep;
        }
        this.addCreep(newCreep);
        return OK;
    }
    run(){
        if(this.size === 0){
            return OK;
        }
        let targets = this.targets;
        if(targets.length === 0 && this.haveTower()){
            for(let creep of this.creeps){
                jobSuicide.run(creep);
            }
            return OK;
        }
        let withdrawTargets = jobWithdraw
            .getTargets(this.spawnRoom)
            .filter((target)=>{
                let energy = getResourceAmount(target);
                let capacity = getCapacity(target);
                if(energy/capacity < 0.5 && energy < 50){
                    return false;
                }
                return target.room.name === this.spawnRoomName;
            });
        for(let creep of this.creeps){
            this.creepRun(creep, targets, withdrawTargets, true);
        }
    }
    /**
     * 
     * @param {AnyCreep} creep 
     * @param {(Structure|ConstructionSite)[]} structures 
     * @param {Boolean} recursive 
     * @returns {CreepActionReturnCode}
     */
    creepRun(creep, structures, withdrawTargets, recursive = true){
        if(creep.spawning){
            return OK;
        }
        if(creep.memory.working){
            if(creep.store[RESOURCE_ENERGY] === 0){
                creep.memory.working = false;
                return this.creepRun(creep, structures, withdrawTargets, false);
            }
            let ret = undefined;
            let damaged = structures.filter((structure)=>jobRepair.filter(structure));
            let filteredDamaged = damaged.filter((structure)=>{
                return structure.hits<2500;
            });
            let target = creep.memory.goto;
            if(target != undefined){
                target = creep.memory.goto.target;
            }
            let isTargetDamaged = damaged.some((structure)=>structure.id == target);
            if(!this.haveTower() && (filteredDamaged.length>0 || isTargetDamaged)){
                if(creep.memory.goto && !isTargetDamaged){
                    damaged = filteredDamaged;
                }
                if(damaged.length > 0){
                    ret = jobRepair.run(creep, damaged);
                    if(ret === OK){
                        creep.say("repair: "+ret);
                        return OK;
                    }
                }
            }

            let sites = structures.filter((structure)=>structure instanceof ConstructionSite);
            if(!this.haveTower()){
                let towerSites = sites.filter((site)=>site.structureType === STRUCTURE_TOWER);
                if(towerSites.length>0){
                    sites = towerSites;
                }
            }
            if(sites.length === 0){
                ret = ERR_NOT_FOUND;
                ret = jobSuicide.run(creep);
            }else{
                ret = jobBuild.run(creep, sites);
            }


            if(ret === ERR_NOT_FOUND || ret === undefined){
                jobSpread.run(creep);
                ret = ERR_INVALID_TARGET;
            }
            creep.say("maint:"+ret);
            return ret;
        }else{
            let ret = jobWithdraw.run(creep, withdrawTargets);
            if(ret === ERR_NOT_FOUND){
                jobSpread.run(creep);
            }
            if(getActualResourceAmount(creep)/getCapacity(creep) >= 0.8 && !(creep.memory.working)){
                creep.memory.working = true;
                if(!recursive){
                    return ERR_FULL;
                }else{
                    return this.creepRun(creep, structures, withdrawTargets, false);
                }
            }
            creep.say("get:"+ret);
            return ret;
        }
    }
}